#import "Texture2D.h"

#define kMaxTextureSize	1024

@interface Texture2D (private)
- (void) resizeTextureToPOW2:(CGImageRef) image;
- (void) getPixelFormat:(CGImageRef) image;
- (CGAffineTransform) scaleTextureToAvailablePOW2:(CGImageRef) img;
- (void) createOpenGLTexture:(GLvoid*) data :(GLenum) filter;
@end


@implementation Texture2D (private)

- (void) resizeTextureToPOW2:(CGImageRef) image
{
	size = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
	width = size.width;
	height = size.height;
	
	if(width > 1)
	{
		NSUInteger pow2 = 1;
		while(pow2 < width)
			pow2 *= 2;
		width = pow2;
	}
	
	if(height > 1)
	{
		NSUInteger pow2 = 1;
		while(pow2 < height)
			pow2 *= 2;
		height = pow2;
	}
	
}

- (void) getPixelFormat:(CGImageRef) img
{
	CGImageAlphaInfo info = CGImageGetAlphaInfo(img);
	BOOL hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || 
					 (info == kCGImageAlphaPremultipliedFirst) || 
					 (info == kCGImageAlphaLast) || 
					 (info == kCGImageAlphaFirst) ? YES : NO);
	
	if(CGImageGetColorSpace(img)) {
		if(hasAlpha)
			pixelFormat = kTexturePixelFormat_RGBA;
		else
			pixelFormat = kTexturePixelFormat_RGB;
	} else 
		pixelFormat = kTexturePixelFormat_Unknow;
	
}

- (CGAffineTransform) scaleTextureToAvailablePOW2:(CGImageRef) image
{
	CGAffineTransform transform = CGAffineTransformIdentity;
	while(width > kMaxTextureSize || height > kMaxTextureSize)
	{
		height /= 2;
		width /= 2;
		transform = CGAffineTransformScale(transform, 0.5, 0.5);
		size.width *= 0.5;
		size.height *= 0.5;
	}
	return transform;
}

- (void) createOpenGLTexture:(GLvoid*) data :(GLenum) filter
{
	glGenTextures(1, &textureID);
	glBindTexture(GL_TEXTURE_2D, textureID);
	
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
	
	switch (pixelFormat) {
		case kTexturePixelFormat_RGBA:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
			break;
		case kTexturePixelFormat_RGB:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_6_5, data);
		case kTexturePixelFormat_Auto:
			glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
			break;
		default:
			[NSException raise:NSInternalInconsistencyException format:@""];
			break;
	}
	
}

@end


@implementation Texture2D
@synthesize textureID, size, width, height, pixelFormat, textureRatio, maxS, maxT;

-(void) dealloc
{
	if(textureID)
		glDeleteTextures(1, &textureID);
	[super dealloc];
}



-(id) initWithImage:(UIImage *)image filter:(GLenum)filter
{
	CGImageRef img = [image CGImage];
	
	[self getPixelFormat:img];
	[self resizeTextureToPOW2:img];
	CGAffineTransform transform = [self scaleTextureToAvailablePOW2:img];
	
	
	
	CGColorSpaceRef colorSpace;
	CGContextRef context = nil;
	GLvoid* data = nil;
	
	switch(pixelFormat) {		
		case kTexturePixelFormat_RGBA:
			colorSpace = CGColorSpaceCreateDeviceRGB();
			data = malloc(height * width * 4);
			context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
			CGColorSpaceRelease(colorSpace);
			break;
			
		case kTexturePixelFormat_RGB:
			colorSpace = CGColorSpaceCreateDeviceRGB();
			data = malloc(height * width * 4);
			context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
			CGColorSpaceRelease(colorSpace);
			break;
			
		case kTexturePixelFormat_Auto:
			data = malloc(height * width);
			context = CGBitmapContextCreate(data, width, height, 8, width, NULL, kCGImageAlphaOnly);
			break;				
		default:
			[NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
	}
	
	//Clear context zone & translate origin to top left corner
	CGContextClearRect(context, CGRectMake(0, 0, width, height));
	CGContextTranslateCTM(context, 0, height - size.height);
	
	if(!CGAffineTransformIsIdentity(transform))
		CGContextConcatCTM(context, transform);
	
	CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(img), CGImageGetHeight(img)), img);
	
	
	// If the pixel format is RGB565 then sort out the image data.
	if(pixelFormat == kTexturePixelFormat_RGB) {
		void* tempData = malloc(height * width * 2);
		unsigned int *inPixel32 = (unsigned int*)data;
		unsigned short *outPixel16 = (unsigned short*)tempData;
		for(int i = 0; i < width * height; ++i, ++inPixel32)
			*outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | 
			((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | 
			((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
		free(data);
		data = tempData;	
	}
	
	
	[self createOpenGLTexture:data :filter];
	
	maxS = size.width / (float)width;
	maxT = size.height / (float)height;
	textureRatio.width = 1.0f / (float)width;
	textureRatio.height = 1.0f / (float)height;
	
	
	CGContextRelease(context);
	free(data);
	return self;
}

@end
